package cn.remex.core;

import cn.remex.RemexConstants;
import cn.remex.core.event.RemexLoginEvent;
import cn.remex.core.event.RemexLogoutEvent;
import cn.remex.core.exception.ServiceCode;
import cn.remex.core.reflect.ReflectUtil;
import cn.remex.core.util.Assert;
import cn.remex.core.util.Judgment;
import cn.remex.core.util.Param;
import cn.remex.core.util.RequestHelper;
import cn.remex.core.util.ThreadHelper;
import cn.remex.db.Database;
import cn.remex.db.model.SysSerialNumber;
import cn.remex.db.model.SysUri;
import cn.remex.db.model.SysRole;
import cn.remex.db.model.SysUser;
import cn.remex.db.rsql.connection.RDBManager;
import cn.remex.db.sql.SqlColumn;
import cn.remex.web.service.BusinessService;
import cn.remex.web.service.ServiceFactory;
import cn.remex.contrib.service.DataService;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

import static cn.remex.core.CoreSvo.HTTP_REQUEST_KEY;
import static cn.remex.db.sql.WhereRuleOper.equal;

/**
 * 名称：
 * 缩写：
 * 用途：
 * Created by LIU on 15/11/26.
 */
public class RemexContext {
    private static final String KEY_UID = "UID";
    private static final String REAL_NAME = "NAME";
    private static final String KEY_TKN = "TKN";
    private static final String KEY_TS = "TS";
    private static Map<String, SysUser> users = new HashMap<>();
    private static Map<String, Map<String,?>> SysUriMapToRole = null;
    public interface CacheDataGroup{
        //所有缓存的数据类别需要一个唯一的key
    }
    public enum SysCacheGroup implements CacheDataGroup{
        //系统内置的缓存组,如果业务中需要自己实现一个枚举实现CacheDataGroup接口
        LoginUserCache
    }


    private static Map<CacheDataGroup, Object> CachePools = new HashMap<>();
    private static Map<CacheDataGroup, Object> CacheTimeout = new HashMap<>();
    static {
        ThreadHelper.run(()->{

        });
    }
    //-----------以下接口用于上下文数据缓存-----------------//
    public static<K, D> D cacheByProvider(CacheDataGroup group, long timeout, Supplier<K> keySupplier, Supplier<D> dataSupplier){
        //缓存逻辑现在用map实现.后面用redis

        Map<K, D> curPool = (Map<K, D>) CachePools.get(group);
        if (null == curPool) {
            CachePools.put(group, curPool = new HashMap<>());
        }
        K key;
        D data = curPool.get(key = keySupplier.get());
        if (null == data) {
            curPool.put(key, data = dataSupplier.get());
        }

        return data;
    }

    public static void clearCache(CacheDataGroup group) {
        Map<?, ?> curPool = (Map<?, ?>) CachePools.put(group, null);
    }



    //-----------以下接口用于用户登录及状态缓存-----------------//
    public static boolean checkToken() {
        String UID = CoreSvo.valCookieValue(KEY_UID); // 用户
        String TS = CoreSvo.valCookieValue(KEY_TS); // 时间戳
        String TKN = CoreSvo.valCookieValue(KEY_TKN); // 凭证

        return !(Judgment.nullOrBlank(UID) || Judgment.nullOrBlank(TS) || Judgment.nullOrBlank(TKN) || !TKN.equals(generateToken(UID, TS)));
    }
    public static Map<String, String> placeToken(SysUser sysUser) {
//        String username = sysUser.getUsername();
        String sysUSerId = sysUser.getId();
        String TS = String.valueOf(LocalTime.now().hashCode());
        String UID = sysUSerId; // 用户
        String TKN = generateToken(UID, TS); // 凭证
        CoreSvo.putCookie(KEY_UID, UID); // 用户
        CoreSvo.putCookie(KEY_TS, TS); // 时间戳
        CoreSvo.putCookie(KEY_TKN, TKN); // 凭证

        Map<String, String> tokeMap = new HashMap<>();
        tokeMap.put(KEY_UID, UID);
        tokeMap.put(KEY_TS, TS);
        tokeMap.put(KEY_TKN, TKN);
        tokeMap.put(REAL_NAME, sysUser.getRealname()); // 用户

        RemexContext.refreshCurUser(sysUSerId);//登录时清理用户缓存

        RemexApplication.publishEvent(new RemexLoginEvent("RemexLoginEvent", sysUser));
        return tokeMap;
    }
    public static void clearToken() {
        SysUser sysUser = RemexContext.obtainCurUser();
        CoreSvo.putCookie(KEY_UID, null); // 用户
        CoreSvo.putCookie(KEY_TS, null); // 时间戳
        CoreSvo.putCookie(KEY_TKN, null); // 凭证

        RemexApplication.publishEvent(new RemexLogoutEvent("RemexLogoutEvent", sysUser));
    }
    public static void refreshCurUser(){
        String sysUSerId = CoreSvo.valCookieValue(KEY_UID);
        if (sysUSerId != null) {
            refreshCurUser(sysUSerId);
        }
    }
    public static SysUser obtainCurUser() {
        String sysUSerId;
        try {
            sysUSerId = CoreSvo.valCookieValue(KEY_UID);
        } catch (Throwable e) {
            RemexConstants.logger.error("从cookie中获取sysUSerId失败，一般发生再测试用例中，此时返回null用户。msg={}" , e.toString());
            return null;
        }
        SysUser curUser;

        if (null == (curUser = users.get(sysUSerId)) && !Judgment.nullOrBlank(sysUSerId)) {
            List<SysUser> ret = Database.createDbCvo(SysUser.class)
//                    .filterBy(SysUser::getUsername, equal, username)
                    .filterById(sysUSerId)
                    .withBase()
                    .withList(SysUser::getRoles, SqlColumn::withBase)
                    .rowCount(100000)
                    .ready().query()
                    .obtainBeans();
            Assert.isTrue(ret.size() <= 1, ServiceCode.FAIL, "用户数据出现重复异常,请联系管理员!");
            if (ret.size() == 1) {
                curUser = ret.get(0);
                users.put(sysUSerId, curUser);
            }
        }
        return curUser;

    }
    public static String obtainClientIP() {
        HttpServletRequest request = (HttpServletRequest) CoreSvo.valLocal(HTTP_REQUEST_KEY);
        return null != request ? RequestHelper.getClientIP(request) : null;
    }

    private static String generateToken(String sysUSerId, String TS) {
        String IP = RequestHelper.getClientIP((HttpServletRequest) CoreSvo.valLocal(HTTP_REQUEST_KEY));
        return String.valueOf(KEY_UID.hashCode()) + sysUSerId.hashCode() + "-" + KEY_TS.hashCode() + TS.hashCode() + "-" + IP.hashCode();
    }
    private static void refreshCurUser(String sysUSerId){
        if(sysUSerId != null){
            SysUser curUser = Database.createDbCvo(SysUser.class)
//                    .filterBy(SysUser::getUsername, equal, sysUSerId)
                    .filterById(sysUSerId)
                    .withList(SysUser::getRoles)
                    .rowCount(100000)
                    .ready().query()
                    .obtainBean();
            users.remove(sysUSerId);
            users.put(sysUSerId, curUser);
        }
    }


    //-----------以下接口用于管理用户权限，尚未开发完成-----------------//
    // uri -> (role.id,?)
    public static Map<String, Map<String, ?>> obtainSysUriMapToRole() {
        if (null != SysUriMapToRole)
            return SysUriMapToRole;
        SysRole root = Database.createDbCvo(SysRole.class).filterBy(SysRole::getName, equal, "root").ready().query().obtainBean();
        SysUriMapToRole = new HashMap<>();
        Database.createDbCvo(SysUri.class).withList(SysUri::getRoles).rowCount(10000)
                .ready().query().obtainObjects(SysUri.class)
                .forEach(sysUri -> {
                    Map<String, ?> curRoles = SysUriMapToRole.get(sysUri.getUri());
                    if (null == curRoles) {
                        curRoles = new HashMap<>();
                        SysUriMapToRole.put(sysUri.getUri(), curRoles);
                        curRoles.put(root.getId(), null);
                    }
                    final Map<String, ?> finalCurRoles = curRoles;
                    if (null != sysUri.getRoles())
                        sysUri.getRoles().forEach(role -> finalCurRoles.put(role.getId(), null));

                });

        return SysUriMapToRole;
    }
    public static boolean isRootUser(){
        SysUser sysUser = RemexContext.obtainCurUser();
        if (sysUser != null)
            for (int i = 0; i < sysUser.getRoles().size(); i++) {
                if ("root".equals(sysUser.getRoles().get(i).getName())) {
                    return true;
                }
            }
        return false;
    }
    // 根据serviceFactory的bs / RsqlCore中的orm and web.xml的配置生成系统服务接口的SysUris列表.*/
    public static List<SysUri> resetAuthority() {
        SysUriMapToRole = new HashMap<>();
        users = new HashMap<>();

        List<SysUri> sysUris = new ArrayList<>();

        //Bs部分
        Param<BusinessService> bsan = new Param<>(null);
        ServiceFactory.getBsMap().forEach((bsName, v) -> ReflectUtil.getAllMethods(v).forEach((methodName, sameNameMethods) ->
                sameNameMethods.stream().filter(m -> (bsan.param = ReflectUtil.getAnnotation(m, BusinessService.class)) != null)
                        .forEach(m -> {
                            SysUri sysUri = new SysUri();
                            sysUri.setUri("/" + bsName + "/" + methodName);
                            sysUri.setUriName(bsan.param.name());
                            sysUri.setUriDesc(bsan.param.desc());
                            sysUris.add(sysUri);
                        })
        ));

        //orm部分的DataTableService
        RDBManager.getLocalSpaceConfig().getOrmBeans().forEach((beanName, beanClass) -> ReflectUtil.getAllMethods(DataService.class).forEach((methodName, sameNameMethods) -> {
            sameNameMethods.stream().filter(m -> (bsan.param = ReflectUtil.getAnnotation(m, BusinessService.class)) != null)
                    .forEach(m -> {
                        SysUri sysUri = new SysUri();
                        sysUri.setUri("/" + DataService.class.getSimpleName() + "/" + beanName + "/" + methodName);
                        sysUris.add(sysUri);
                    });
        }));

        sysUris.forEach(sysUri -> Database.createDbCvo(SysUri.class).withBase().ready().store(sysUri));
        return sysUris;
    }
    public static SysUri saveSysUri(String uri, BusinessService bsan) {
        SysUri sysUri = Database.select(SysUri.class).filterBy(SysUri::getUri, equal, uri).withBase().execute().obtainBean();
        if (null == sysUri) {sysUri = new SysUri();}
        sysUri.setUri(uri);
        sysUri.setUriName(bsan.name());
        sysUri.setUriDesc(bsan.desc());
        sysUri.setRoles(new ArrayList<>());

        BusinessService.ServiceRole[] roles = bsan.authRoles();
        for (BusinessService.ServiceRole role : roles) {
            SysRole sysRole = checkAndSaveRole(role);
            sysUri.getRoles().add(sysRole);
        }

        Database.store(SysUri.class)
                .assignBean(sysUri)
                .execute().assertEffectOneRow(ServiceCode.FAIL, "根据BusinessService注解自动生成资源及其对应角色权限关系时发生错误");

        return sysUri;
    }
    public static SysRole checkAndSaveRole(BusinessService.ServiceRole serviceRole) {
        SysRole curRole = null;
        if (serviceRole != null) {
            int id = serviceRole.getId();
            Assert.isTrue(id >= 1000, ServiceCode.FAIL, "角色编号不能小于1000");

            curRole = Database.select(SysRole.class).filterBy(SysRole::getId, equal, id).withBase().execute().obtainBean();
            if (null == curRole) {
                curRole = new SysRole();
                curRole.assignNewId(String.valueOf(id));
            }
            curRole.setName(serviceRole.name());

            Database.store(SysRole.class)
                    .assignBean(curRole)
                    .execute().assertEffectOneRow(ServiceCode.FAIL,"根据BusinessService注解自动生成角色时发生错误");

        }
        return curRole;
    }


    //-----------以下接口用于统一序列号-----------------//
    public static Object createSerialNumber(String type, String tag){
        return SysSerialNumber.createSerialNumber(type, tag);
    }

}
